// app/api/data-room/[projectId]/files/route.ts import { NextRequest, NextResponse } from 'next/server'; import { getServerSession } from 'next-auth/next'; import { authOptions } from '@/app/api/auth/[...nextauth]/route'; import { fileItems } from '@/db/schema'; import { and, eq, isNull, desc, asc, sql } from 'drizzle-orm'; import db from '@/db/db'; // 파일 목록 조회 export async function GET( request: NextRequest, { params }: { params: Promise<{ projectId: string }> } ) { try { const { projectId } = await params; // 세션 확인 const session = await getServerSession(authOptions); if (!session?.user) { return NextResponse.json({ error: '인증이 필요합니다' }, { status: 401 }); } // URL 파라미터 파싱 const { searchParams } = new URL(request.url); const parentId = searchParams.get('parentId'); const category = searchParams.get('category'); const type = searchParams.get('type'); // 'file' | 'folder' | 'all' const sortBy = searchParams.get('sortBy') || 'name'; const sortOrder = searchParams.get('sortOrder') || 'asc'; // 기본 조회 조건 설정 const conditions = []; // 프로젝트 ID는 필수 conditions.push(eq(fileItems.projectId, projectId)); // parentId 조건 추가 if (parentId && parentId !== 'null') { conditions.push(eq(fileItems.parentId, parentId)); } else { // parentId가 없으면 최상위 항목만 조회 conditions.push(isNull(fileItems.parentId)); } // 카테고리 필터 if (category) { conditions.push(eq(fileItems.category, category)); } // 타입 필터 (file, folder, all) if (type && type !== 'all') { if (type === 'file' || type === 'folder') { conditions.push(eq(fileItems.type, type)); } } // 파일 목록 조회 const files = await db .select() .from(fileItems) .where(conditions.length > 0 ? and(...conditions) : undefined) .orderBy( // 폴더를 먼저 표시 desc(fileItems.type), // 그 다음 정렬 기준 적용 sortBy === 'name' ? (sortOrder === 'asc' ? asc(fileItems.name) : desc(fileItems.name)) : sortBy === 'updatedAt' ? (sortOrder === 'asc' ? asc(fileItems.updatedAt) : desc(fileItems.updatedAt)) : sortBy === 'size' ? (sortOrder === 'asc' ? asc(fileItems.size) : desc(fileItems.size)) : asc(fileItems.name) // 기본값 ); // 파트너사 사용자의 경우 접근 가능한 파일만 필터링 let filteredFiles = files; if (session.user.domain === 'partners') { // 현재는 모든 파일을 볼 수 있도록 설정 // 필요시 추가 필터링 로직 구현 filteredFiles = files; } // 응답 데이터 구성 const response = { files: filteredFiles.map(file => ({ id: file.id, projectId: file.projectId, parentId: file.parentId, name: file.name, type: file.type, path: file.path || '/', category: file.category || 'uncategorized', size: file.size || 0, mimeType: file.mimeType || '', uploadedBy: file.uploadedBy, uploadedByDomain: file.uploadedByDomain || 'default', createdAt: file.createdAt, updatedAt: file.updatedAt, // 내부 사용자에게만 추가 정보 제공 ...(session.user.domain !== 'partners' && { createdBy: file.createdBy, updatedBy: file.updatedBy, }), })), count: filteredFiles.length, parentId: parentId || null, // 현재 경로 정보 (브레드크럼용) currentPath: parentId ? await getCurrentPath(parentId, projectId) : [], }; return NextResponse.json(response); } catch (error) { console.error('파일 목록 조회 오류:', error); return NextResponse.json( { error: '파일 목록을 불러오는데 실패했습니다', details: process.env.NODE_ENV === 'development' ? error.message : undefined }, { status: 500 } ); } } // 현재 경로 정보 가져오기 (브레드크럼용) async function getCurrentPath( folderId: string, projectId: string ): Promise> { try { const path: Array<{ id: string; name: string }> = []; let currentId: string | null = folderId; let depth = 0; const maxDepth = 10; while (currentId && depth < maxDepth) { const result = await db .select({ id: fileItems.id, name: fileItems.name, parentId: fileItems.parentId, }) .from(fileItems) .where( and( eq(fileItems.id, currentId), eq(fileItems.projectId, projectId), eq(fileItems.type, 'folder') ) ) .limit(1); const folder = result[0]; if (!folder) break; // 경로 앞에 추가 (역순이므로) path.unshift({ id: folder.id, name: folder.name }); currentId = folder.parentId; depth++; } return path; } catch (error) { console.error('경로 조회 오류:', error); return []; } } // 파일 검색 (POST) export async function POST( request: NextRequest, { params }: { params: Promise<{ projectId: string }> } ) { try { const { projectId } = await params; const session = await getServerSession(authOptions); if (!session?.user) { return NextResponse.json({ error: '인증이 필요합니다' }, { status: 401 }); } const body = await request.json(); const { searchTerm, filters } = body; if (!searchTerm || searchTerm.trim().length < 2) { return NextResponse.json({ error: '검색어는 2글자 이상 입력해주세요' }, { status: 400 }); } // 검색 쿼리 생성 const searchConditions = [ eq(fileItems.projectId, projectId), sql`LOWER(${fileItems.name}) LIKE LOWER(${'%' + searchTerm + '%'})` ]; // 필터 적용 if (filters?.category) { searchConditions.push(eq(fileItems.category, filters.category)); } if (filters?.type) { searchConditions.push(eq(fileItems.type, filters.type)); } const searchResults = await db .select() .from(fileItems) .where(and(...searchConditions)) .orderBy(desc(fileItems.type), asc(fileItems.name)) .limit(50); // 최대 50개 결과 // 파트너사 권한 필터링 let filteredResults = searchResults; if (session.user.domain === 'partners') { // 현재는 모든 결과 반환 filteredResults = searchResults; } return NextResponse.json({ results: filteredResults.map(file => ({ id: file.id, projectId: file.projectId, parentId: file.parentId, name: file.name, type: file.type, path: file.path || '/', category: file.category || 'uncategorized', size: file.size || 0, mimeType: file.mimeType || '', updatedAt: file.updatedAt, })), count: filteredResults.length, searchTerm, }); } catch (error) { console.error('파일 검색 오류:', error); return NextResponse.json( { error: '파일 검색에 실패했습니다', details: process.env.NODE_ENV === 'development' ? error.message : undefined }, { status: 500 } ); } }